home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / daemons / migd / migd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-10-17  |  44.9 KB  |  1,767 lines

  1. /* 
  2.  * migd.c --
  3.  *
  4.  *    Routines to manage duties of the host-specific load average daemon.
  5.  *    This includes supporting the host-specific pdev, and tracking load
  6.  *    average and idle time.
  7.  *
  8.  * Copyright 1990 Regents of the University of California
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /sprite/src/daemons/migd/RCS/migd.c,v 2.4 90/10/17 08:44:21 ouster Exp $ SPRITE (Berkeley)";
  20. #endif /* not lint */
  21.  
  22. #include <sprite.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <errno.h>
  27. #include <signal.h>
  28. #include <setjmp.h>
  29. #include <fs.h>
  30. #include <option.h>
  31. #include <syslog.h>
  32. #include <status.h>
  33. #include <sysStats.h>
  34. #include <host.h>
  35. #include <sys/stat.h>
  36. #include <sys/file.h>
  37. #include <sys/signal.h>
  38. #include <sys/wait.h>
  39. #include <sys/time.h>
  40. #include <sys/resource.h>
  41. #include <kernel/sched.h>
  42. #include <kernel/procMigrate.h>
  43. #include <bit.h>
  44. #include <pwd.h>
  45. #include "migd.h"
  46. #include "migPdev.h"
  47. #include "global.h"
  48.  
  49. /*
  50.  * Indication of whether this process is the global master or
  51.  * a per-host daemon.
  52.  */
  53.  
  54. int migd_GlobalMaster = 0;
  55.  
  56.  
  57. int migd_Debug = 0;        /* Enable debugging info? */
  58. int migd_Quit = 0;        /* Flag to indicate we should quit */
  59.  
  60. Fs_TimeoutHandler migd_TimeoutToken; /* Token for current timeout. */
  61.  
  62. static int migdGlobalDesc = -1;    /* Descriptor for communications with
  63.                    global server. */
  64.  
  65. static Mig_Info currentInfo;    /* Current load info, passed to server */
  66. static Mig_LoadVector *curVecPtr; /* Pointer into part of currentInfo that
  67.                    * is periodically updated. */
  68.  
  69. int migd_AlwaysAccept = 0;    /* Override defaults for accepting processes? */
  70. int migd_NeverAccept = 0;    /* Likewise. */
  71.  
  72. #define HIGH_TO_LOW_TICKS ((unsigned)0xffffffff)
  73.  
  74. /*
  75.  * Minimum queue length that we can store.
  76.  */
  77. #define MIN_LENGTH 0.00001
  78.  
  79. /*
  80.  * Scheduler statistics at some instant in time (saved until next set of
  81.  * statistics is obtained).
  82.  */
  83.  
  84. typedef struct {
  85.     int     lowTicks;
  86.     int     highTicks;
  87.     int        queueLength;
  88.     int        pad;
  89.     struct timeval time;
  90. } Stats;
  91.  
  92. /*
  93.  * Keep track of both utilization and ready-queue length when load
  94.  * averages are used.
  95.  */
  96.  
  97. typedef struct {
  98.     double     utilization;
  99.     double    queueLength;
  100. } Load;
  101.  
  102. /*
  103.  * Constants specific to gathering load average statistics.  (The
  104.  * weights may be modified with -D options but are not
  105.  * user-specifiable on the command line.)  The load interval is the
  106.  * number of seconds between sampling the utilization and ready queue
  107.  * lengths, and the write interval is the maximum number of seconds
  108.  * that may pass between updating the global daemon.  These constants
  109.  * are defined in mig.h
  110.  */
  111.  
  112.  
  113. #define WEIGHT1 0.9200444146293232     /* exp(-1/12) */
  114. #define WEIGHT2 0.9834714538216174     /* exp(-1/60) */
  115. #define WEIGHT3 0.9944598480048967     /* exp(-1/180) */
  116.  
  117. double migd_Weights[] = {WEIGHT1, WEIGHT2, WEIGHT3};
  118. int migd_Verbose = 1;
  119. int migd_LoadInterval = MIG_LOAD_INTERVAL;
  120. int migd_WriteInterval = MIG_GLOBAL_UPDATE_INTERVAL;
  121.  
  122. /*
  123.  * Define global variables used for debugging & statistics gathering.
  124.  */
  125. int migd_LogToFiles = 0;
  126. int migd_DontFork = 0;
  127. int migd_DoStats = 1;
  128. int migd_NeverEvict = 0;
  129. int migd_NeverRunGlobal = 0;
  130.  
  131. /*
  132.  * Define arbitrary thresholds (see loadavg.h).
  133.  *
  134.  * All load values must be below their corresponding THRESHOLD_LOW* to
  135.  * start accepting foreign processes, but once they are being accepted,
  136.  * there are different thresholds (THRESHOLD_HIGH*) for each average: if
  137.  * any one of them is exceeded, stop accepting foreign processes.  (This
  138.  * really needs to be generalized to handle a multiprocessor.)
  139.  *
  140.  * INPUT_THRESHOLD is the number of seconds that a node must
  141.  * be idle for it to accept migrated processes.  
  142.  */
  143.  
  144. #define THRESHOLD_LOW0 0.75
  145. #define THRESHOLD_LOW1 1.0
  146. #define THRESHOLD_LOW2 1.5
  147. #define THRESHOLD_HIGH0 2.5
  148. #define THRESHOLD_HIGH1 2.75
  149. #define THRESHOLD_HIGH2 3.0
  150. #ifndef INPUT_THRESHOLD
  151. #define INPUT_THRESHOLD 30
  152. #endif
  153.  
  154. /* 
  155.  * Initialize our parameters.  Fill in 0 for the things we have to get
  156.  * at run time.
  157.  */
  158. Mig_SystemParms migd_Parms = {
  159.     0,                   /* criteria, obtained from kernel */
  160.     0,                   /* version, obtained from kernel  */
  161.     INPUT_THRESHOLD,                    /* noInput   */
  162.     0,                            /* pad       */
  163.     {THRESHOLD_LOW0, THRESHOLD_LOW1, THRESHOLD_LOW2},   /* minThresh */
  164.     {THRESHOLD_HIGH0, THRESHOLD_HIGH1, THRESHOLD_HIGH2}    /* maxThresh */
  165. };
  166.  
  167. /*
  168.  * Set the default timeout for eviction, in seconds.  
  169.  */
  170. #ifndef EVICT_TIMEOUT
  171. #define EVICT_TIMEOUT 300
  172. #endif /* EVICT_TIMEOUT */
  173.  
  174. /*
  175.  * Set the default write timeout, in seconds.
  176.  */
  177. #ifndef WRITE_TIMEOUT
  178. #define WRITE_TIMEOUT 10
  179. #endif /* WRITE_TIMEOUT */
  180.  
  181. /*
  182.  * Set the number of times we'll try to contact the global daemon before
  183.  * giving up.
  184.  */
  185. #ifndef MAX_GLOBAL_CONTACTS
  186. #define MAX_GLOBAL_CONTACTS 100
  187. #endif /* MAX_GLOBAL_CONTACTS */
  188.  
  189.  
  190. static unsigned int maxIdle;    /* maximum idle ticks per unit time */
  191. static int refuseMigration;    /* whether kernel is refusing migrations */
  192.  
  193. static int ignoreInput;        /* whether we should ignore input time when
  194.                    deciding when to accept migration */
  195. static int ignoreLoad;        /* ditto for load average */
  196. static int writeRate;        /* number of iterations before updating global
  197.                    server */
  198.  
  199. static int numLowPris = 0;    /* Number of low-priority processes on this
  200.                    host.  XXX Set by searching process
  201.                    table. */
  202.  
  203. static Stats stats[2];        /* statistics -- current and last -- initialized
  204.                  * once and then reset each callback.
  205.                  */
  206.  
  207. static    jmp_buf    writejmp;        /* Used for timeouts on writes. */
  208. static  struct itimerval timeOutTimer;     /* Set to WRITE_TIME_OUT. */
  209. static  struct itimerval noTimer;    /* Set to 0. */
  210.  
  211. static int Reap();
  212. static int AlarmHandler();
  213. static int WriteAlarm();
  214. static void CalculateLoads();
  215. static void GetStats();
  216. static void OutputLoads();
  217. static void ParseMigStatus();
  218. static int  CheckMessages();
  219.  
  220. static int ContactGlobal();
  221. static int CreateGlobal();
  222. static void GetNewParms();
  223. #ifdef FAST_SELECT
  224. static void HandleException();
  225. #endif /* FAST_SELECT */
  226.  
  227. /*
  228.  *----------------------------------------------------------------------
  229.  *
  230.  * Migd_Init --
  231.  *
  232.  *    Initialize data structures for a per-host migration daemon.
  233.  * 
  234.  * Results:
  235.  *    0 for success, or a sprite ReturnStatus for failure.
  236.  *
  237.  * Side effects:
  238.  *    The pseudo-device to talk to the master is opened.  If no
  239.  *    master exists, this process tries to create a master.
  240.  *
  241.  *----------------------------------------------------------------------
  242.  */
  243.  
  244. int
  245. Migd_Init()
  246. {
  247.     int realErrno;
  248.     char fileName[FS_MAX_NAME_LENGTH];
  249.     int status;
  250.     struct stat atts;
  251.     Time    period;
  252.     struct timeval time;
  253.  
  254.     if (migd_Debug > 0) {
  255.     fprintf(stderr, "Migd_Init -\n");
  256.     }
  257.  
  258.     signal(SIGCHLD, Reap);
  259.  
  260.     ParseMigStatus();
  261.     
  262.     /*
  263.      * Set up the initial Mig_Info structure, and variables we're going
  264.      * to access periodically.  By default, we're active when we
  265.      * start up, but later there's a check for idleTime being less
  266.      * than the threshold if it's also equal to the time since we booted.
  267.      */
  268.     curVecPtr = ¤tInfo.loadVec;
  269.     bzero((char *) ¤tInfo, sizeof(Mig_Info));
  270.     currentInfo.hostID = migd_HostID;
  271.     currentInfo.migVersion = migd_Parms.version;
  272.     currentInfo.maxProcs = 1;    /* XXX number of processors */
  273.     curVecPtr->allowMigration = migd_AlwaysAccept;
  274.     currentInfo.state = refuseMigration ?  MIG_HOST_REFUSES : MIG_HOST_ACTIVE;
  275.  
  276.     status = gettimeofday(&time, (struct timezone *) NULL);
  277.     if (status == -1) {
  278.     SYSLOG1(LOG_ERR, "Error in gettimeofday: %s", strerror(errno));
  279.     exit(1);
  280.     }
  281.     curVecPtr->timestamp = time.tv_sec;
  282.  
  283.     /*
  284.      * Get bootstamp.  Note, this should be a kernel call instead of a
  285.      * check on the file, but this will do for the "moment".
  286.      */
  287.     (void) sprintf(fileName, "/hosts/%s/boottime", migd_HostName);
  288.  
  289.     status = stat(fileName, &atts);
  290.     if (status == 0) {
  291.     currentInfo.bootTime = atts.st_mtime;
  292.     } else {
  293.     SYSLOG2(LOG_ERR, "Error getting boot time from %s: %s\n", fileName,
  294.            strerror(errno));
  295.     currentInfo.bootTime = 0;
  296.     }
  297.  
  298.  
  299.     /*
  300.      * Check to see if the global daemon is prohibited.
  301.      */
  302.     if (!migd_NeverRunGlobal) {
  303.     (void) sprintf(fileName, 
  304.                "/hosts/%s/global-migd-prohibited", migd_HostName);
  305.     status = stat(fileName, &atts);
  306.     if (status == 0) {
  307.         syslog(LOG_INFO, "We will not run the global master.\n");
  308.         migd_NeverRunGlobal = 1;
  309.     }
  310.     }
  311.  
  312.  
  313.     writeRate = migd_WriteInterval / migd_LoadInterval;
  314.     
  315.     GetStats(&stats[0], &curVecPtr->noInput,
  316.          &curVecPtr->foreignProcs);
  317.     curVecPtr->timestamp = stats[0].time.tv_sec;
  318.     
  319.  
  320.     if (ContactGlobal() < 0) {
  321.     realErrno = errno;
  322.     fprintf(stderr, "Migd_Init - Unable to contact global daemon: %s\n",
  323.            strerror(errno));
  324.     errno = realErrno;
  325.     return(-1);
  326.     }
  327.     if (MigPdev_OpenMaster() < 0) {
  328.     realErrno = errno;
  329.     fprintf(stderr, "Migd_Init - Unable to initialize pdev %s: %s\n",
  330.            migd_LocalPdevName, strerror(errno));
  331.     errno = realErrno;
  332.     return(-1);
  333.     }
  334.  
  335.     syslog(LOG_INFO, "running.");
  336.     period.seconds = migd_LoadInterval;
  337.     period.microseconds = 0;
  338.  
  339.     /*
  340.      * Set up timeout for writes.
  341.      */
  342.     timeOutTimer.it_interval.tv_sec = 0;
  343.     timeOutTimer.it_interval.tv_usec = 0;
  344.     timeOutTimer.it_value.tv_sec = WRITE_TIMEOUT;
  345.     timeOutTimer.it_value.tv_usec = 0;
  346.     noTimer.it_interval.tv_sec = 0;
  347.     noTimer.it_interval.tv_usec = 0;
  348.     noTimer.it_value.tv_sec = 0;
  349.     noTimer.it_value.tv_usec = 0;
  350.  
  351.  
  352.     migd_TimeoutToken = Fs_TimeoutHandlerCreate(period, TRUE, Migd_GatherLoad,
  353.                         (ClientData) NULL);
  354.  
  355.  
  356.     if (migd_Debug > 0) {
  357.     fprintf(stderr, "Migd_Init - returning 0\n");
  358.     }
  359.  
  360.     return(0);
  361. }
  362.  
  363.  
  364.  
  365. /*
  366.  *----------------------------------------------------------------------
  367.  *
  368.  * Migd_End --
  369.  *
  370.  *    Terminate service of the master end of a host-specific pdev.
  371.  *
  372.  * Results:
  373.  *    None.
  374.  *
  375.  * Side effects:
  376.  *    The pdev is removed and closed.
  377.  *
  378.  *----------------------------------------------------------------------
  379.  */
  380.  
  381. void
  382. Migd_End()
  383. {
  384.     if (migd_Debug > 0) {
  385.     fprintf(stderr, "Migd_End -\n");
  386.     }
  387.  
  388.     MigPdev_End();
  389.  
  390. }
  391.  
  392.  
  393. /*
  394.  *----------------------------------------------------------------------
  395.  *
  396.  * ParseMigStatus --
  397.  *
  398.  *    Parse the kernel status by checking individual bits.  This
  399.  *    is performed at start-up and anytime the migd_Parms structure
  400.  *    is updated.
  401.  *
  402.  * Results:
  403.  *    None.
  404.  *
  405.  * Side effects:
  406.  *    File-global variables are updated.
  407.  *
  408.  *----------------------------------------------------------------------
  409.  */
  410.  
  411. static void
  412. ParseMigStatus()
  413. {
  414.     int kernelState = migd_Parms.criteria;
  415.     
  416.     if ((kernelState & PROC_MIG_IMPORT_ALL) != PROC_MIG_IMPORT_ALL) {
  417.     refuseMigration = 1;
  418.     } else {
  419.     refuseMigration = 0;
  420.     }
  421.     ignoreLoad = kernelState & PROC_MIG_IMPORT_ANYLOAD;
  422.     ignoreInput = kernelState & PROC_MIG_IMPORT_ANYINPUT;
  423. }
  424.  
  425.  
  426.  
  427.  
  428. /*
  429.  *----------------------------------------------------------------------
  430.  *
  431.  * ContactGlobal --
  432.  *
  433.  *    Open up the global pseudo-device as a client, and inform
  434.  *    the master that we are a daemon.  If we can't open it, we sleep
  435.  *    a random period of time and try again before returning an error.
  436.  *    This reduces the likelihood of many hosts trying to open the
  437.  *    pdev as a master when one fails.
  438.  *
  439.  * Results:
  440.  *    0 for successful completion, -1 for error, in which case
  441.  *    errno indicates the nature of the error.  The special errno
  442.  *    ENODEV indicates that the master is nonexistent.
  443.  *
  444.  * Side effects:
  445.  *    Opens pseudo-device as client.
  446.  *
  447.  *----------------------------------------------------------------------
  448.  */
  449.  
  450. static int
  451. ContactGlobal()
  452. {
  453.     int sleepTime;
  454.     int status;
  455.     int retries;
  456.     int ioctlRetries;
  457.     int realErrno;
  458.     int success = 0;
  459.     static int firstContact = 1; /* First time we are trying to reach the
  460.                     global daemon? */
  461.     int first;             /* Value of firstContact at time of call. */
  462.     int t;
  463.     
  464.     t = time(0);
  465.     if (migd_Debug > 1) {
  466.     fprintf(stderr, "ContactGlobal - %s\n", ctime(&t));
  467.     }
  468.  
  469.     /*
  470.      * Set a temporary variable to track firstContact, and reset it so
  471.      * any subsequent calls have the updated value.  This avoids the
  472.      * need to reset it before every return statement.
  473.      */
  474.     first = firstContact;
  475.     if (firstContact) {
  476.     /*
  477.      * First time we've been called. Set up seed for random
  478.      * numbers.
  479.      */
  480.     srandom(getpid());
  481.     firstContact = 0;
  482.     } 
  483.  
  484.     /*
  485.      * Clean up any old descriptor.
  486.      */
  487.     if (migdGlobalDesc >= 0) {
  488.     Fs_EventHandlerDestroy(migdGlobalDesc);
  489.     (void) close(migdGlobalDesc);
  490.     }
  491.     
  492.     sleepTime = (random() & 07) + 1;
  493.     for (retries = 1;
  494.      retries <= MAX_GLOBAL_CONTACTS && !migd_Quit && !success;
  495.      retries++) {
  496.     migdGlobalDesc = open(migd_GlobalPdevName, O_RDWR, 0);
  497.     if (migdGlobalDesc < 0) {
  498.         if (migd_Debug > 2) {
  499.         fprintf(stderr, "ContactGlobal - sleeping %d seconds\n",
  500.                sleepTime);
  501.         }
  502.         sleep(sleepTime);
  503.         sleepTime *= 2;
  504.         migdGlobalDesc = open(migd_GlobalPdevName, O_RDWR, 0);
  505.     }
  506.     if (migdGlobalDesc < 0) {
  507.         if (migd_Debug > 0) {
  508.         fprintf(stderr,
  509.                "ContactGlobal: couldn't open %s: %s\n",
  510.                migd_GlobalPdevName, strerror(errno));
  511.         }
  512.         /*
  513.          * If errno is ENOENT, there is not currently a master, anywhere.
  514.          * (When the master exits it removes the pdev.)  EIO 
  515.          * may mean the daemon crashed.  EINVAL may mean the daemon's host
  516.          * crashed.  We special case EIO due to a race condition
  517.          * between recovery and starting daemons.
  518.          */
  519.         if (errno == ENOENT || errno == EIO || errno == EINVAL) {
  520.         if (retries == MAX_GLOBAL_CONTACTS - 1 && errno != ENOENT) {
  521.             /*
  522.              * We're getting desperate here.  We can't open
  523.              * the file, but we should be able to.  Remove
  524.              * the pdev and try one last time to create the
  525.              * master, since it may be that the host running
  526.              * the master has crashed and the name server
  527.              * is continually returning a bad status to us.
  528.              * We risk clobbering someone else who has successfully
  529.              * opened the pdev just before us, but there's a small
  530.              * window of vulnerability and by this time we're sleeping
  531.              * a long time.  
  532.              */
  533.             (void) unlink(migd_GlobalPdevName);
  534.         }
  535.         if (!migd_NeverRunGlobal) {
  536.             if (CreateGlobal() < 0) {
  537.             return(-1);
  538.             }
  539.         }
  540.         /*
  541.          * Go to start of for loop, trying to open pdev.
  542.          */
  543.         continue;
  544.         } else {
  545.         realErrno = errno;
  546.         fprintf(stderr, "Migd_Init - Unable to contact master of global pdev: %s\n",
  547.                strerror(errno));
  548.         errno = realErrno;
  549.         return(-1);
  550.         }
  551.     } else {
  552.         /*
  553.          * We've successfully opened the pdev.
  554.          * Try to tell the global master that we're a daemon.  It may say
  555.          * DEV_BUSY, which means that there is already a daemon.  In that
  556.          * case, it tells the other daemon to go away, and we will keep
  557.          * trying.  In some cases we may get an error doing the ioctl,
  558.          * such as a stale handle, in which case we close the file and
  559.          * go to the top again.
  560.          */
  561.         for (ioctlRetries = 1; ioctlRetries <= MAX_GLOBAL_CONTACTS;
  562.          ioctlRetries++) {
  563.         status = Fs_IOControl(migdGlobalDesc, IOC_MIG_DAEMON, sizeof(Mig_Info),
  564.                       (char *) ¤tInfo,
  565.                       0, (char *) NULL);
  566.         if (status == DEV_BUSY) {
  567.             if (migd_Debug > 0) {
  568.             fprintf(stderr, "ContactGlobal - ioctl returned busy.\n");
  569.             }
  570.             sleepTime = ((random() & 07) + 1) * ioctlRetries;
  571.             if (migd_Debug > 2) {
  572.             fprintf(stderr, "ContactGlobal - sleeping %d seconds\n",
  573.                    sleepTime);
  574.             }
  575.             sleep(sleepTime);
  576.         } else {
  577.             /*
  578.              * An error we can't deal with, or SUCCESS.
  579.              */
  580.             break;
  581.         }
  582.         }
  583.         if (status != SUCCESS) {
  584.         SYSLOG1(LOG_ERR,
  585.                "ContactGlobal: warning: error during ioctl to global master: %s\n",
  586.                Stat_GetMsg(status));
  587.         errno = Compat_MapCode(status);
  588.         close(migdGlobalDesc);
  589.         } else {
  590.         /*
  591.          * We did it!  Break out of the inner for loop, and the
  592.          * success flag will break us out of the outer loop.
  593.          */
  594.         success = 1;
  595.         break;
  596.         }
  597.     }
  598.     }
  599.     if (!success) {
  600.     realErrno = errno;
  601.     SYSLOG0(LOG_ERR, "unable to contact master; giving up.\n");
  602.     errno = realErrno;
  603.     return(-1);
  604.     }
  605.  
  606. #ifdef FAST_SELECT
  607.     Fs_EventHandlerCreate(migdGlobalDesc, FS_READ|FS_EXCEPTION,
  608.               HandleException, (ClientData) NULL);
  609. #endif /* FAST_SELECT */
  610.  
  611.     if (migd_Debug > 1) {
  612.     fprintf(stderr, "ContactGlobal - completed successfully\n");
  613.     }
  614.  
  615.     return (0);
  616. }
  617.  
  618.  
  619. /*
  620.  *----------------------------------------------------------------------
  621.  *
  622.  * Reap --
  623.  *
  624.  *    Reap any exited child processes to make sure they don't stick
  625.  *    around.
  626.  *
  627.  * Results:
  628.  *    None.
  629.  *
  630.  * Side effects:
  631.  *    None.
  632.  *
  633.  *----------------------------------------------------------------------
  634.  */
  635.  
  636. static int
  637. Reap()
  638. {
  639.     union wait status;
  640.  
  641.     if (migd_Debug > 3) {
  642.     fprintf(stderr, "Reap - \n");
  643.     }
  644.     
  645.     while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0) {
  646.     }
  647.     return(0);
  648. }
  649.  
  650.  
  651. /*
  652.  *----------------------------------------------------------------------
  653.  *
  654.  * WriteAlarm --
  655.  *
  656.  *    Routine to service a SIGALRM signal during a write, which
  657.  *    requires that we longjmp to interrupt the write.
  658.  *    This routine also disables the alarm, letting the caller reenable
  659.  *    it when appropriate.
  660.  *
  661.  * Results:
  662.  *    None.
  663.  *
  664.  * Side effects:
  665.  *    The alarm is disabled.
  666.  *
  667.  *----------------------------------------------------------------------
  668.  */
  669. static int
  670. WriteAlarm()
  671. {
  672.     
  673.     alarm(0);
  674.     syslog(LOG_INFO, "Write to global daemon timed out.");
  675.     (void) signal (SIGALRM, SIG_IGN);
  676.     longjmp(writejmp, 1);
  677.     /* THIS DOES NOT RETURN. */
  678. }
  679.  
  680. /*
  681.  *----------------------------------------------------------------------
  682.  *
  683.  * CreateGlobal --
  684.  *
  685.  *    Fork off a process to try and become the global master.  
  686.  *
  687.  * Results:
  688.  *    0 for successful completion, -1 for error, in which case
  689.  *    errno indicates the nature of the error.  The parent
  690.  *    will return success as long as it can successfully fork.  That
  691.  *     doesn't necessarily mean the master side of the global pdev
  692.  *    has been opened, at least by the child, but the parent will go
  693.  *     ahead and try to contact the master once again in any case.
  694.  *
  695.  * Side effects:
  696.  *    A new process is created.
  697.  *
  698.  *----------------------------------------------------------------------
  699.  */
  700.  
  701. static int
  702. CreateGlobal()
  703. {
  704.     int pid;
  705.     
  706.     if (migd_Debug > 2) {
  707.     fprintf(stderr, "CreateGlobal -\n");
  708.     }
  709.  
  710.     if (migd_DontFork) {
  711.     fprintf(stderr, "CreateGlobal - becoming master; start a new child.\n");
  712.     pid = 0;
  713.     } else {
  714.     pid = fork();
  715.     if (pid < 0) {
  716.         fprintf(stderr, "CreateGlobal - couldn't fork\n");
  717.         return(-1);
  718.     }
  719.     }
  720.     if (pid > 0) {
  721.     /*
  722.      * Give the child a chance to set things up.  We should be awakened
  723.      * by a signal if the child should exit prematurely.
  724.      */
  725.     sleep(5);
  726.     return(0);
  727.     }
  728.  
  729.     /*
  730.      * We are the child, and will try to become the global master.
  731.      */
  732.     (void) signal(SIGCHLD, SIG_DFL);
  733.     MigPdev_CloseAllStreams();
  734.  
  735.     if (Global_Init() < 0) {
  736.     exit(1);
  737.     }
  738.     
  739.     if (migd_Debug > 0) {
  740.     fprintf(stderr, "CreateGlobal - we are the global master, pid %x\n",
  741.            getpid());
  742.     }
  743.  
  744.     Migd_HandleRequests();
  745.  
  746.     /*
  747.      * If we reach this point we were signalled or in some other way
  748.      * told to exit.
  749.      */
  750.  
  751.     Global_End();
  752.     DATE();
  753.     exit(0);
  754.  
  755. #ifdef lint
  756.     return(0); /* keep lint happy */
  757. #endif
  758. }
  759.  
  760.  
  761.  
  762. /*
  763.  *----------------------------------------------------------------------
  764.  *
  765.  * Migd_HandleRequests --
  766.  *
  767.  *    Main loop to call Fs_Dispatch; used by both per-host daemons and
  768.  *     global master.
  769.  *
  770.  * Results:
  771.  *    None.
  772.  *
  773.  * Side effects:
  774.  *    Calls to Fs_Dispatch invoke callbacks.
  775.  *
  776.  *----------------------------------------------------------------------
  777.  */
  778.  
  779. void
  780. Migd_HandleRequests()
  781. {
  782.     while(1) {
  783.     if (migd_Quit && !migd_GlobalMaster) {
  784.         break;
  785.     }
  786.     Fs_Dispatch();
  787.     }
  788. }
  789.  
  790.  
  791. /*
  792.  *----------------------------------------------------------------------
  793.  *
  794.  * Migd_GatherLoad --
  795.  *
  796.  *    Update the current load averages and report them to the master
  797.  *    daemon.
  798.  *
  799.  * Results:
  800.  *    None.
  801.  *
  802.  * Side effects:
  803.  *    Writes to master daemon.
  804.  *
  805.  *----------------------------------------------------------------------
  806.  */
  807.  
  808. void
  809. Migd_GatherLoad()
  810. {
  811.     int oldAllow;
  812.     int    oldInput;
  813.     int    oldForeign;
  814.     static int nextStat = 1;
  815.     static int iteration = 0;
  816.     int numWritten;
  817.     int error;
  818.     int status;
  819.     
  820.     
  821.     oldAllow = curVecPtr->allowMigration;
  822.     oldInput = curVecPtr->noInput;
  823.     oldForeign = curVecPtr->foreignProcs;
  824.     if (migd_Debug > 2) {
  825.     fprintf(stderr, "Migd_GatherLoad - time %d, oldAllow %d, oldInput %d\n",
  826.            time((int *) NULL), oldAllow, oldInput);
  827.     }
  828.     GetStats(&stats[nextStat], &curVecPtr->noInput, &curVecPtr->foreignProcs);
  829.     curVecPtr->timestamp = stats[nextStat].time.tv_sec;
  830.     nextStat = (nextStat + 1) % 2;
  831.     CalculateLoads(stats, nextStat, curVecPtr);
  832.  
  833.     if ((oldInput > migd_Parms.noInput) &&
  834.     (curVecPtr->noInput < migd_Parms.noInput) &&
  835.     !ignoreInput && !migd_NeverEvict && !refuseMigration) {
  836.     Migd_Evict(TRUE);
  837.     }
  838.  
  839.     /*
  840.      * Send the new load vector to the global daemon periodically,
  841.      * or if our migration status changes, or if the number of
  842.      * foreign processes goes from zero to non-zero or vice-versa.
  843.      * This way the global daemon can track things like the last use
  844.      * of a machine by a process that won't release the host when it
  845.      * finishes.
  846.      */
  847.     if (iteration == 0 || (oldAllow != curVecPtr->allowMigration) ||
  848.     (oldForeign > 0 && curVecPtr->foreignProcs == 0) ||
  849.     (oldForeign == 0 && curVecPtr->foreignProcs > 0)) {
  850.     if (migd_Debug > 2) {
  851.         fprintf(stderr,
  852.            "Notifying global server, iteration %d, oldAllow %d, newAllow %d, oldForeign %d, newForeign %d.\n",
  853.            iteration, oldAllow, curVecPtr->allowMigration,
  854.            oldForeign, curVecPtr->foreignProcs);
  855.     }
  856.     iteration = 0;
  857.  
  858.     /*
  859.      * Get the kernel's variable determining whether to refuse
  860.      * migrations.  We keep rechecking periodically in case it changes.
  861.      */
  862.  
  863.     status = Sys_Stats(SYS_PROC_MIGRATION, SYS_PROC_MIG_GET_STATE,
  864.                (Address) &migd_Parms.criteria);
  865.     if (status != SUCCESS) {
  866.         SYSLOG1(LOG_ERR, "Error in Sys_Stats getting migration state: %s.\n",
  867.             Stat_GetMsg(status));
  868.         exit(Compat_MapCode(status));
  869.     }
  870.     ParseMigStatus();
  871.  
  872.     if (curVecPtr->lengths[1] >= 1.0)  {
  873.         struct timeval tv;
  874.         struct timeval curTime;
  875.         
  876.         /*
  877.          * The 5-minute load average is over 1.  This could
  878.          * happen if there is a long-running process but it
  879.          * also seems to happen without anything running.
  880.          * Sleep a short period of time to try to
  881.          * keep from being in lock-step with someone else.  There's
  882.          * nothing too magical about the number except that it's
  883.          * intended to be something that other processes are unlikely
  884.          * to sleep for.  
  885.          */
  886.         tv.tv_sec = 0;
  887.         tv.tv_usec = ((random() % 999) + 1) * 1000;
  888. ;
  889.         if (migd_Debug > 2) {
  890.         if (gettimeofday(&curTime,
  891.                  (struct timezone *) NULL) < 0) {
  892.             perror("Error in gettimeofday");
  893.             exit(1);
  894.         }
  895.         fprintf(stderr,
  896.             "Sleeping %d usec to avoid lock step, time %d.%d.\n",
  897.             tv.tv_usec, curTime.tv_sec, curTime.tv_usec);
  898.         }
  899.         if (select(0, (int *) NULL, (int *) NULL, (int *) NULL,
  900.               &tv) < 0) {
  901.         if (migd_Debug > 2) {
  902.             perror("select");
  903.         }
  904.         }
  905.         if (migd_Debug > 2) {
  906.         if (gettimeofday(&curTime,
  907.                  (struct timezone *) NULL) < 0) {
  908.             perror("Error in gettimeofday");
  909.             exit(1);
  910.         }
  911.         fprintf(stderr, "Time is now %d.%d.\n", curTime.tv_sec,
  912.             curTime.tv_usec);
  913.         }
  914.     }
  915.     if (migd_Debug > 3) {
  916.         fprintf(stderr, "Writing vector to global daemon.\n");
  917.     }
  918.  
  919.     /*
  920.      * OK, here's the tricky part.  We don't want our write to wait
  921.      * indefinitely, so we set an alarm.  But just waking up won't
  922.      * cause Fs_Write to return an error, so we have to longjmp.
  923.      * So we set the signal handler, set the timer, and setjmp, then
  924.      * after the write we reverse the process.
  925.      */
  926.     if (setjmp(writejmp)) {
  927.         numWritten = -1;
  928.         errno = EIO;
  929.     } else {
  930.         if ((int) signal(SIGALRM, WriteAlarm) < 0) {
  931.         syslog(LOG_ERR, "Error setting signal handler: %s.\n",
  932.                strerror(errno));
  933.         exit(1);
  934.         }
  935.         if (setitimer(ITIMER_REAL, &timeOutTimer,
  936.               (struct itimerval *) NULL) == -1) {
  937.         syslog(LOG_ERR, "Error setting interval timer: %s.\n",
  938.                strerror(errno));
  939.         exit(1);
  940.         }
  941.         numWritten = write(migdGlobalDesc, (char *) curVecPtr,
  942.                    sizeof(Mig_LoadVector));
  943.     }
  944.     error = errno;
  945.     if (setitimer(ITIMER_REAL, &noTimer,
  946.               (struct itimerval *) NULL) == -1) {
  947.         syslog(LOG_ERR, "Error disabling interval timer: %s.\n",
  948.            strerror(errno));
  949.         exit(1);
  950.     }
  951.     (void) signal(SIGALRM, SIG_IGN);
  952.     errno = error;
  953.  
  954.     /*
  955.      * Now we're back to where we would be if all we'd done was
  956.      * write(), with errno and numWritten set to appropriate values.
  957.      */
  958.  
  959.     if (migd_Debug > 3) {
  960.         fprintf(stderr, "Write returned value %d.\n", numWritten);
  961.     }
  962.     if (numWritten < 0) {
  963.         if (migd_Debug > 0) {
  964.         fprintf(stderr, "Error %d writing to global daemon: %s.\n",
  965.                error, strerror(error));
  966.         }
  967.         close(migdGlobalDesc);
  968.         if (migd_Quit || ContactGlobal() < 0) {
  969.         fprintf(stderr, "Exiting.\n");
  970.         exit(1);
  971.         }
  972.     } else if (numWritten != sizeof(Mig_LoadVector)) {
  973.         SYSLOG2(LOG_WARNING, "short write to global daemon of %d/%d bytes.\n",
  974.            numWritten, sizeof(Mig_LoadVector));
  975.     }
  976.     iteration = 0;
  977.  
  978.     /*
  979.      * Check on currentInfo.state in case we have to reconnect to the
  980.      * global daemon or a user process reads the Mig_Info struct from
  981.      * us.
  982.      */
  983.     if (curVecPtr->allowMigration &&
  984.         currentInfo.state == MIG_HOST_ACTIVE) {
  985.         currentInfo.state = MIG_HOST_IDLE;
  986.     } else if (!curVecPtr->allowMigration &&
  987.            currentInfo.state == MIG_HOST_IDLE) {
  988.         currentInfo.state = refuseMigration ?
  989.         MIG_HOST_REFUSES : MIG_HOST_ACTIVE;
  990.     }
  991.  
  992.     if (CheckMessages() >= 0) {
  993.         if (migd_Debug > 0) {
  994.         fprintf(stderr,
  995.             "This host is being reclaimed by order of global migration daemon.\n");
  996.         }
  997.         Migd_Evict(FALSE);
  998.     }
  999.  
  1000.     }
  1001.  
  1002.     iteration = (iteration + 1) % writeRate;
  1003. }
  1004.  
  1005.  
  1006. /*
  1007.  *----------------------------------------------------------------------
  1008.  *
  1009.  * CalculateLoads --
  1010.  *
  1011.  *    Given the number of idle ticks at two different times, calculate
  1012.  *    the utilization of the computer during that period of time and
  1013.  *    average with weighted values of utilization over different times.
  1014.  *
  1015.  *    The same calculation is performed for ready queue length.
  1016.  *
  1017.  * Results:
  1018.  *    The calculated utilization & average queue lengths are returned in
  1019.  *    *curVecPtr.  
  1020.  *
  1021.  * Side effects:
  1022.  *    None.
  1023.  *
  1024.  *----------------------------------------------------------------------
  1025.  */
  1026.  
  1027. static void
  1028. CalculateLoads(stats, nextStat, curVecPtr)
  1029.     Stats stats[];
  1030.     int nextStat;
  1031.     Mig_LoadVector *curVecPtr;
  1032. {
  1033.     double currentLoad;
  1034.     register Stats *oldStatsPtr;
  1035.     register Stats *newStatsPtr;
  1036.     unsigned lowTicks;
  1037.     double lowNormal;
  1038.     double actualTime;
  1039.     int i;
  1040.     static int init = 0;
  1041.  
  1042.     if (!init) {
  1043.     init = 1;
  1044.     for (i = 0; i < MIG_NUM_LOAD_VALUES; i++) {
  1045.         curVecPtr->utils[i] = 0;
  1046.         curVecPtr->lengths[i] = 0;
  1047.     }
  1048.     }
  1049.     
  1050.     if (nextStat == 0) {
  1051.     oldStatsPtr = stats;
  1052.     newStatsPtr = &(stats[1]);
  1053.     } else {
  1054.     newStatsPtr = stats;
  1055.     oldStatsPtr = &(stats[1]);
  1056.     }
  1057.     if (newStatsPtr->highTicks != oldStatsPtr->highTicks) {
  1058.     lowTicks =  HIGH_TO_LOW_TICKS - oldStatsPtr->lowTicks +
  1059.         newStatsPtr->lowTicks;
  1060.     } else {
  1061.     lowTicks = newStatsPtr->lowTicks - oldStatsPtr->lowTicks;
  1062.     }
  1063.  
  1064. #define TV_TO_FLOAT(t) \
  1065.     (((double) (t).tv_sec) + ((double) (t).tv_usec) / 1000000)
  1066.  
  1067.     actualTime = TV_TO_FLOAT(newStatsPtr->time) -
  1068.     TV_TO_FLOAT(oldStatsPtr->time);
  1069.     lowNormal = ((double) lowTicks) * migd_LoadInterval / actualTime;
  1070.     if (migd_Debug > 3) {
  1071.     (void) fprintf(stderr,
  1072.                "Lowticks=%u, actualTime = %f, lowNormal = %.0f.\n",
  1073.                lowTicks, actualTime, lowNormal);
  1074.     }
  1075.  
  1076.     /*
  1077.      * It is possible to fluctuate a bit and wind up with
  1078.      * lowTicks > maxIdle.  Correct if so.
  1079.      */
  1080.  
  1081.     if (lowNormal > maxIdle) {
  1082.     currentLoad = 0.;
  1083.     } else {
  1084.     currentLoad = 100 * (1 - lowNormal/maxIdle);
  1085.     }
  1086.  
  1087.     if (migd_Debug > 3) {
  1088.     (void) fprintf(stderr,
  1089.                "OldStatsPtr->lowTicks=%u, newStatsPtr->lowTicks=%u.\n",
  1090.                oldStatsPtr->lowTicks, newStatsPtr->lowTicks);
  1091.     (void) fprintf(stderr,
  1092.                "Idle ticks %u/%u => %6.2f%% Utilization, Queue length %d.\n",
  1093.                lowTicks,
  1094.                maxIdle,
  1095.                currentLoad,
  1096.                newStatsPtr->queueLength);
  1097.     (void) fprintf(stderr, "allowMigration %d noInput %d\n",
  1098.                curVecPtr->allowMigration, curVecPtr->noInput);
  1099.     fflush(stderr);
  1100.     }
  1101.  
  1102.     for (i = 0; i < MIG_NUM_LOAD_VALUES; i++) {
  1103.     curVecPtr->utils[i] = migd_Weights[i] * curVecPtr->utils[i] +
  1104.         (1 - migd_Weights[i]) * currentLoad;
  1105.     curVecPtr->lengths[i] = migd_Weights[i] * curVecPtr->lengths[i] +
  1106.         (1 - migd_Weights[i]) * newStatsPtr->queueLength;
  1107.     if (curVecPtr->lengths[i] < MIN_LENGTH) {
  1108.         curVecPtr->lengths[i] = 0;
  1109.     }
  1110.     if (migd_Debug > 3) {
  1111.         fprintf(stderr, "index %d util %d length %.2f\n",
  1112.             i, curVecPtr->utils[i], curVecPtr->lengths[i]);
  1113.     }
  1114.     }
  1115.     if (migd_Debug > 1) {
  1116.     (void) fflush(stderr);
  1117.     }
  1118.  
  1119.     /*
  1120.      * Check for our state changing from nonmigratable to migratable or
  1121.      * vice-versa.
  1122.      */
  1123.  
  1124.     if (!migd_AlwaysAccept) {
  1125.     if (curVecPtr->allowMigration) {
  1126.         if ((((curVecPtr->noInput < migd_Parms.noInput) &&
  1127.           (curVecPtr->noInput <
  1128.            curVecPtr->timestamp - currentInfo.bootTime - migd_LoadInterval)) &&
  1129.          !ignoreInput) ||
  1130.         refuseMigration) {
  1131.         curVecPtr->allowMigration = 0;
  1132.         if (refuseMigration) {
  1133.             currentInfo.state = MIG_HOST_REFUSES;
  1134.         } else {
  1135.             currentInfo.state = MIG_HOST_ACTIVE;
  1136.         }
  1137.         } else if (!ignoreLoad){
  1138.         for (i = 0; i < MIG_NUM_LOAD_VALUES; i++) {
  1139.             /*
  1140.              * Compare the load to the threshold, ignoring
  1141.              * low-priority processes.
  1142.              */
  1143.             if (curVecPtr->lengths[i] - numLowPris >
  1144.             migd_Parms.maxThresh[i]) {
  1145.             if (migd_Debug > 3) {
  1146.                 fprintf(stderr, "Load too high\n");
  1147.             }
  1148.             curVecPtr->allowMigration = 0;
  1149.             break;
  1150.             } else if (migd_Debug > 3 && curVecPtr->lengths[i] >
  1151.                    migd_Parms.maxThresh[i]) {
  1152.             fprintf(stderr, "Load low enough because of %d low priority processes\n",
  1153.                 numLowPris);
  1154.             }
  1155.         }
  1156.         }
  1157.         /*
  1158.          * Allow migration if we're ignoring input or we've been
  1159.          * idle for a while or we've been idle as long as we've been up.
  1160.          * Don't allow it if overridden by a flag, though.
  1161.          */
  1162.     } else if ((! curVecPtr->allowMigration) &&
  1163.            (ignoreInput || (curVecPtr->noInput >= migd_Parms.noInput) ||
  1164.             (curVecPtr->noInput >=
  1165.              curVecPtr->timestamp - currentInfo.bootTime - migd_LoadInterval)) &&
  1166.            !refuseMigration) {
  1167.         curVecPtr->allowMigration = 1;
  1168.         currentInfo.state = MIG_HOST_IDLE;
  1169.         if (!ignoreLoad) {
  1170.         for (i = 0; i < MIG_NUM_LOAD_VALUES; i++) {
  1171.             if (curVecPtr->lengths[i] > migd_Parms.minThresh[i]) {
  1172.             curVecPtr->allowMigration = 0;
  1173.             currentInfo.state = MIG_HOST_ACTIVE;
  1174.             }
  1175.         }
  1176.         }
  1177.     }
  1178.     }
  1179. }
  1180.  
  1181.  
  1182. /*
  1183.  *----------------------------------------------------------------------
  1184.  *
  1185.  * GetStats --
  1186.  *
  1187.  *    Get the current number of idle ticks and the length of the
  1188.  *    ready queue.
  1189.  *
  1190.  * Results:
  1191.  *     The number of ticks and length of the ready queue are
  1192.  *    returned in *statsPtr.  The current time, time since last user
  1193.  *    input, and foreign process count are also returned.
  1194.  *
  1195.  * Side effects:
  1196.  *    The global variable 'maxIdle' is initialized to
  1197.  *    idleTicksPerSecond * MIG_LOAD_INTERVAL.
  1198.  *
  1199.  *----------------------------------------------------------------------
  1200.  */
  1201.  
  1202. static void
  1203. GetStats(statsPtr, noInputPtr, foreignPtr)
  1204.     Stats *statsPtr;
  1205.     int *noInputPtr;
  1206.     int *foreignPtr;
  1207. {
  1208.     Sched_Instrument stats;
  1209.     static int init = 0;
  1210.     int status;
  1211.     Proc_MigStats migStats;
  1212.  
  1213.     Sys_Stats(SYS_SCHED_STATS, 0, (Address) &stats);
  1214.     statsPtr->lowTicks = stats.processor[0].idleTicksLow;
  1215.     statsPtr->highTicks = stats.processor[0].idleTicksOverflow;
  1216.     statsPtr->queueLength = stats.numReadyProcesses;
  1217.  
  1218.     *noInputPtr = stats.noUserInput.seconds;
  1219.     if (migd_Debug > 3) {
  1220.     fprintf(stderr, "GetStats: no input in %d seconds.\n",
  1221.            stats.noUserInput.seconds);
  1222.     }
  1223.     /*
  1224.      * Check for bogus idle time.
  1225.      */
  1226.     if (*noInputPtr < 0) {
  1227.     *noInputPtr = 0;
  1228.     }
  1229.  
  1230.     status = gettimeofday(&statsPtr->time, (struct timezone *) NULL);
  1231.     if (status == -1) {
  1232.     perror("Error in gettimeofday");
  1233.     exit(1);
  1234.     }
  1235.  
  1236.     if (!init) {
  1237.     maxIdle = stats.processor[0].idleTicksPerSecond * migd_LoadInterval;
  1238.     init = 1;
  1239.     }
  1240.  
  1241.     /*
  1242.      * Get a copy of the proc statistics record.  Make sure it's zeroed in
  1243.      * case the kernel provides us with a shorter (older) structure.
  1244.      */
  1245.  
  1246.     bzero((Address) &migStats, sizeof(migStats));
  1247.     status = Sys_Stats(SYS_PROC_MIGRATION, SYS_PROC_MIG_GET_STATS,
  1248.                (Address) &migStats);
  1249.     if (status != SUCCESS) {
  1250.     SYSLOG1(LOG_ERR, "Error from Sys_Stats: %s.\n", Stat_GetMsg(status));
  1251.     exit(1);
  1252.     }
  1253.     *foreignPtr = migStats.foreign;
  1254.  
  1255.  
  1256. }
  1257.  
  1258. /*
  1259.  *----------------------------------------------------------------------
  1260.  *
  1261.  * Migd_GetLocalLoad --
  1262.  *
  1263.  *    Return the current migInfo structure -- this is used by anyone
  1264.  *    contacting the local daemon and using read to get the local
  1265.  *    load.
  1266.  *
  1267.  * Results:
  1268.  *    Fills buffer with Mig_Info struct.
  1269.  *
  1270.  * Side effects:
  1271.  *    None.
  1272.  *
  1273.  *----------------------------------------------------------------------
  1274.  */
  1275.  
  1276. void
  1277. Migd_GetLocalLoad(buffer)
  1278.     char *buffer;
  1279. {
  1280.     Mig_Info *infoPtr;
  1281.  
  1282.     infoPtr = (Mig_Info *) buffer;
  1283.  
  1284.     *infoPtr = currentInfo;
  1285. }
  1286.  
  1287.  
  1288.  
  1289. /*
  1290.  *----------------------------------------------------------------------
  1291.  *
  1292.  * HandleException --
  1293.  *
  1294.  *    Do something when the migdGlobalDesc becomes selectable.  This
  1295.  *    can happen if the stream gets an error of some sort, or
  1296.  *    if it becomes readable (meaning we should contact the daemon about
  1297.  *     something).
  1298.  *
  1299.  * Results:
  1300.  *    None.
  1301.  *
  1302.  * Side effects:
  1303.  *    The daemon may be contacted, or the stream to it reopened.
  1304.  *
  1305.  *----------------------------------------------------------------------
  1306.  */
  1307.  
  1308. /* ARGSUSED */
  1309. #ifdef FAST_SELECT
  1310. static void
  1311. HandleException(clientData, streamID, eventMask)
  1312.     ClientData clientData;
  1313.     int streamID;
  1314.     int eventMask;
  1315. {
  1316.     if (migd_Debug > 0) { 
  1317.     fprintf(stderr, "HandleException -\n");
  1318.     }
  1319.     if (eventMask & FS_EXCEPTION) {
  1320.     fprintf(stderr,
  1321.            "Exception occurred talking to global daemon.  Reopening contact.\n");
  1322.     (void) close(streamID);
  1323.     if (migd_Quit || ContactGlobal() < 0) {
  1324.         DATE();
  1325.         SYSLOG0(LOG_ERR, "Exiting.\n");
  1326.         exit(1);
  1327.     }
  1328.     }
  1329.     if (eventMask & FS_READ) {
  1330.     GetNewParms();
  1331.     }
  1332. }
  1333. #endif /* FAST_SELECT */
  1334.  
  1335.  
  1336. /*
  1337.  *----------------------------------------------------------------------
  1338.  *
  1339.  * GetNewParms --
  1340.  *
  1341.  *    Get new parameters from the global daemon.
  1342.  *
  1343.  * Results:
  1344.  *    None.
  1345.  *
  1346.  * Side effects:
  1347.  *    The migd_Parms structure is updated, and global variables
  1348.  *    are reset accordingly.
  1349.  *
  1350.  *----------------------------------------------------------------------
  1351.  */
  1352.  
  1353. /* ARGSUSED */
  1354. static void
  1355. GetNewParms(clientData, streamID, eventMask)
  1356.     ClientData clientData;
  1357.     int streamID;
  1358.     int eventMask;
  1359. {
  1360.     fprintf(stderr, "GetNewParms called, doing nothing yet.\n");
  1361. }
  1362.  
  1363.  
  1364. /*
  1365.  *----------------------------------------------------------------------
  1366.  *
  1367.  * Migd_SetParms --
  1368.  *
  1369.  *    Set the parameters used by this daemon (only).
  1370.  *
  1371.  * Results:
  1372.  *    0 for success, or an errno indicating the error.
  1373.  *
  1374.  * Side effects:
  1375.  *    The migd_Parms structure is updated, and global variables
  1376.  *    are reset accordingly.
  1377.  *
  1378.  *----------------------------------------------------------------------
  1379.  */
  1380.  
  1381. /* ARGSUSED */
  1382. int
  1383. Migd_SetParms(cltPtr, command, inBuffer, inBufSize, outBuffer,
  1384.            outBufSizePtr)
  1385.     Migd_OpenStreamInfo *cltPtr;/* Information about the client making
  1386.                    the request. */
  1387.     int command;        /* Ignored. */
  1388.     char *inBuffer;        /* Buffer to get arguments from. */
  1389.     int inBufSize;        /* Size of the input buffer. */
  1390.     char *outBuffer;        /* Buffer to place results, not used. */
  1391.     int *outBufSizePtr;        /* Size of the output buffer, set to 0. */
  1392. {
  1393.     if (migd_Debug > 1) {
  1394.     fprintf(stderr, "Migd_SetParms called.\n");
  1395.     }
  1396.  
  1397.     /*
  1398.      * XXX need to add this here.
  1399.      */
  1400.     return(EINVAL);
  1401. }
  1402.     
  1403.  
  1404. /*
  1405.  *----------------------------------------------------------------------
  1406.  *
  1407.  * Migd_GetParms --
  1408.  *
  1409.  *    Get the parameters used by this daemon.
  1410.  *
  1411.  * Results:
  1412.  *    0 for success, or an errno indicating the error.
  1413.  *    The parameters structure, and the size of the buffer, are returned.
  1414.  *
  1415.  * Side effects:
  1416.  *    None.
  1417.  *
  1418.  *----------------------------------------------------------------------
  1419.  */
  1420.  
  1421. /* ARGSUSED */
  1422. int
  1423. Migd_GetParms(cltPtr, command, inBuffer, inBufSize, outBuffer,
  1424.            outBufSizePtr)
  1425.     Migd_OpenStreamInfo *cltPtr;/* Information about the client making
  1426.                    the request. */
  1427.     int command;        /* Ignored. */
  1428.     char *inBuffer;        /* Not used. */
  1429.     int inBufSize;        /* Not used. */
  1430.     char *outBuffer;        /* Buffer to place results. */
  1431.     int *outBufSizePtr;        /* Size of the output buffer. */
  1432. {
  1433.     if (migd_Debug > 1) {
  1434.     fprintf(stderr, "Migd_GetParms called.\n");
  1435.     }
  1436.  
  1437.     /*
  1438.      * XXX need to add this here.
  1439.      */
  1440.     return(EINVAL);
  1441. }
  1442.     
  1443.  
  1444. /*
  1445.  *----------------------------------------------------------------------
  1446.  *
  1447.  * AlarmHandler --
  1448.  *
  1449.  *    Routine to service a SIGALRM signal for eviction.
  1450.  *    This routine disables
  1451.  *    the alarm (letting the caller reenable it when appropriate).
  1452.  *
  1453.  * Results:
  1454.  *    None.
  1455.  *
  1456.  * Side effects:
  1457.  *    The alarm is disabled.
  1458.  *
  1459.  *----------------------------------------------------------------------
  1460.  */
  1461. static int
  1462. AlarmHandler()
  1463. {
  1464.     
  1465.     alarm(0);
  1466.     if (migd_Debug > 0) {
  1467.     SYSLOG0(LOG_INFO, "Eviction didn't finish in a timely fashion.");
  1468.     }
  1469.     (void) signal (SIGALRM, SIG_IGN);
  1470. }
  1471.  
  1472. /*
  1473.  *----------------------------------------------------------------------
  1474.  *
  1475.  * Migd_Evict --
  1476.  *
  1477.  *    Perform a system call to evict foreign processes, with an
  1478.  *    appropriate timeout to ensure we don't block forever.
  1479.  *
  1480.  * Results:
  1481.  *    None.
  1482.  *
  1483.  * Side effects:
  1484.  *    Forks a process to perform the eviction.  If the global
  1485.  *    migd_Verbose variable is set, information about each process
  1486.  *    evicted is printed on the syslog.  If notify is set, it does
  1487.  *    an ioctl to tell the global daemon; if not, then that means we're
  1488.  *    evicting on orders of the global daemon.
  1489.  *
  1490.  *----------------------------------------------------------------------
  1491.  */
  1492.  
  1493. void
  1494. Migd_Evict(notify)
  1495.     int notify;            /* Whether to notify global daemon  */
  1496. {
  1497.     ReturnStatus status;
  1498.     struct itimerval itimer, oldItimer;
  1499.     int (*oldHandler) ();
  1500. #define NUM_PCBS 256
  1501.     Proc_PCBInfo infos[NUM_PCBS];
  1502.     register Proc_PCBInfo *infoPtr;
  1503.     int pcbsUsed;
  1504.     int i;
  1505.     int pid;
  1506.     Host_Entry *hostPtr;
  1507.     struct passwd *pwPtr;
  1508.     int state;
  1509.     int numEvicted = 0;
  1510.  
  1511.  
  1512.     if (migd_Debug > 1) {
  1513.     fprintf(stderr, "Migd_Evict -\n");
  1514.     }
  1515.     pid = fork();
  1516.     if (pid < 0) {
  1517.     fprintf(stderr, "Migd_Evict - couldn't fork\n");
  1518.     return;
  1519.     } else if (pid > 0) {
  1520.     return;
  1521.     }
  1522.  
  1523.     if (migd_Verbose) {
  1524.     /*
  1525.      * Dump the entire process table into our memory.
  1526.      */
  1527.  
  1528.     status = Proc_GetPCBInfo(0, NUM_PCBS-1, PROC_MY_HOSTID,
  1529.                  sizeof(Proc_PCBInfo),
  1530.                  infos, (Proc_PCBArgString *) NULL, &pcbsUsed);
  1531.     if (status != SUCCESS) {
  1532.         fprintf(stderr, "Couldn't read process table: %s\n",
  1533.            Stat_GetMsg(status));
  1534.         exit(1);
  1535.     }
  1536.  
  1537.     for (i = 0, infoPtr = infos; i < pcbsUsed;
  1538.          i++, infoPtr++) {
  1539.         if ((infoPtr->genFlags & PROC_FOREIGN) &&
  1540.         !(infoPtr->genFlags & PROC_DONT_MIGRATE)) {
  1541.         switch (infoPtr->state) {
  1542.             case PROC_RUNNING:
  1543.             case PROC_READY:
  1544.             case PROC_WAITING:
  1545.             case PROC_SUSPENDED: {
  1546.             hostPtr = Host_ByID(infoPtr->peerHostID);
  1547.             pwPtr = getpwuid(infoPtr->userID);
  1548.             if (hostPtr != (Host_Entry *) NULL &&
  1549.                 pwPtr != (struct passwd *) NULL) {
  1550.                 syslog(LOG_INFO,
  1551.                    "Evicting process %x (%s@%s)",
  1552.                    infoPtr->peerProcessID, pwPtr->pw_name,
  1553.                    hostPtr->name);
  1554.                 numEvicted++;
  1555.             }
  1556.             }
  1557.             break;
  1558.             default: {
  1559.             break;
  1560.             }
  1561.         }
  1562.         }
  1563.     }
  1564.     Host_End();
  1565.     endpwent();
  1566.     }
  1567.     /*
  1568.      * At this point, make the kernel call to get
  1569.      * all processes at this point in time.  They may be slightly
  1570.      * different from the ones we found using "ps"-like calls,
  1571.      * but that was just for information anyway -- this is the real
  1572.      * mccoy.  (For this reason, often there will be no processes evicted.)
  1573.      * We also set up a timer in case we block too long.
  1574.      */
  1575.     itimer.it_interval.tv_sec = 0;
  1576.     itimer.it_interval.tv_usec = 0;
  1577.     itimer.it_value.tv_sec = EVICT_TIMEOUT;
  1578.     itimer.it_value.tv_usec = 0;
  1579.  
  1580.     if ((int) signal(SIGALRM, AlarmHandler) < 0) {
  1581.     syslog(LOG_ERR, "Error setting signal handler: %s.\n",
  1582.            strerror(errno));
  1583.     exit(1);
  1584.     }
  1585.     if (setitimer(ITIMER_REAL, &itimer,
  1586.           (struct itimerval *) NULL) == -1) {
  1587.     syslog(LOG_ERR, "Error setting interval timer: %s.\n",
  1588.            strerror(errno));
  1589.     exit(1);
  1590.     }
  1591.     status = Proc_Migrate(PROC_ALL_PROCESSES, 0);
  1592.     if (status != SUCCESS && status != GEN_ABORTED_BY_SIGNAL) {
  1593.     syslog(LOG_ERR, "Error evicting foreign processes: %s\n",
  1594.            Stat_GetMsg(status));
  1595.     } else if (migd_Verbose && numEvicted > 0) {
  1596.     syslog(LOG_INFO, "Evicted %d processes.\n", numEvicted);
  1597.     }
  1598.     (void) setitimer(ITIMER_REAL, &noTimer, (struct itimerval *) NULL);
  1599.     (void) signal(SIGALRM, SIG_IGN);
  1600.  
  1601.     if (notify) {
  1602.     /*
  1603.      * Notify the global daemon that eviction has occurred.
  1604.      */
  1605.     state = MIG_HOST_ACTIVE;
  1606.     status = Fs_IOControl(migdGlobalDesc, IOC_MIG_CHANGE, sizeof(int),
  1607.                   (char *) &state,
  1608.                   0, (char *) NULL);
  1609.     if (status != SUCCESS) {
  1610.         if (migd_Debug > 0) {
  1611.         fprintf(stderr,
  1612.             "Error doing MIG_CHANGE ioctl with global daemon: %s.\n",
  1613.             Stat_GetMsg(status));
  1614.         }
  1615.     }
  1616.     }
  1617.  
  1618.  
  1619.     exit(0);
  1620. }
  1621.  
  1622.  
  1623. /*
  1624.  *----------------------------------------------------------------------
  1625.  *
  1626.  * Migd_EvictIoctl --
  1627.  *
  1628.  *    Ioctl interface to Migd_Evict routine.  May be called by user
  1629.  *    program to force an eviction.
  1630.  *
  1631.  * Results:
  1632.  *    0 is returned as the value of the ioctl.  The number of foreign
  1633.  *    processes on the machine is returned in outBuffer.
  1634.  *
  1635.  * Side effects:
  1636.  *    Any foreign processes are evicted.
  1637.  *
  1638.  *----------------------------------------------------------------------
  1639.  */
  1640.  
  1641. /* ARGSUSED */
  1642. int
  1643. Migd_EvictIoctl(cltPtr, command, inBuffer, inBufSize, outBuffer,
  1644.            outBufSizePtr)
  1645.     Migd_OpenStreamInfo *cltPtr;/* Information about the client making
  1646.                    the request. */
  1647.     int command;        /* Ignored. */
  1648.     char *inBuffer;        /* Not used. */
  1649.     int inBufSize;        /* Not used. */
  1650.     char *outBuffer;        /* Buffer to place results. */
  1651.     int *outBufSizePtr;        /* Size of the output buffer. */
  1652. {
  1653.     int *intPtr;
  1654.     
  1655.     if (migd_Debug > 1) {
  1656.     fprintf(stderr, "Migd_EvictIoctl called.\n");
  1657.     }
  1658.  
  1659.     if (*outBufSizePtr < sizeof(int)) {
  1660.     if (migd_Debug > 0) {
  1661.         SYSLOG2(LOG_WARNING,
  1662.            "Migd_EvictIoctl: bad output buffer size (%d) from process %x\n",
  1663.            *outBufSizePtr, cltPtr->processID);
  1664.     }
  1665.     return(EINVAL);
  1666.     }
  1667.     if (migd_GlobalMaster) {
  1668.     return(EINVAL);
  1669.     }
  1670.  
  1671.     intPtr = (int *) outBuffer;
  1672.     *intPtr = curVecPtr->foreignProcs;
  1673.     *outBufSizePtr = sizeof(int);
  1674.     
  1675.     Migd_Evict(TRUE);
  1676.     return(0);
  1677. }
  1678.  
  1679.     
  1680.  
  1681. /*
  1682.  *----------------------------------------------------------------------
  1683.  *
  1684.  * CheckMessages --
  1685.  *
  1686.  *    Check for messages from the global daemon.  
  1687.  *
  1688.  * Results:
  1689.  *    -1 if no messages or on error.  Otherwise returns the value of the
  1690.  *    message, which is an integer.
  1691.  *    Obtains at most one message per call.
  1692.  *
  1693.  * Side effects:
  1694.  *    May do ioctl to server.
  1695.  *
  1696.  *----------------------------------------------------------------------
  1697.  */
  1698. static int
  1699. CheckMessages()
  1700. {
  1701.     static int *bitArray = NULL;
  1702.     static int bitSize = 0;
  1703.     int numReady;
  1704.     int status;
  1705.     int msgHostID;
  1706.     struct timeval time;
  1707.     
  1708. #ifdef DEBUG
  1709.     fprintf(stderr, "CheckMessages called.\n");
  1710. #endif                /* DEBUG */
  1711.  
  1712.     if (migdGlobalDesc < 0) {
  1713. #ifdef DEBUG
  1714.     fprintf(stderr, "CheckMessages: no pdev connection.\n");
  1715. #endif                /* DEBUG */
  1716.     return(-1);
  1717.     }
  1718.     if (bitSize <= migdGlobalDesc) {
  1719.     bitArray = Bit_Expand(migdGlobalDesc + 1, bitSize, bitArray);
  1720.     bitSize = migdGlobalDesc + 1;
  1721.     }
  1722.         
  1723.     Bit_Set(migdGlobalDesc, bitArray);
  1724.     time.tv_sec = 0;
  1725.     time.tv_usec = 0;
  1726. #ifdef DEBUG
  1727.     fprintf(stderr, "CheckMessages: calling select.\n");
  1728. #endif                /* DEBUG */
  1729.     numReady = select(bitSize, bitArray, (int *) NULL, (int *) NULL,
  1730.               &time);
  1731. #ifdef DEBUG
  1732.     fprintf(stderr, "CheckMessages: select returned %d.\n", numReady);
  1733. #endif                /* DEBUG */
  1734.     if (numReady <= 0) {
  1735.     return(-1);
  1736.     } else {
  1737. #ifdef DEBUG
  1738.     fprintf(stderr, "CheckMessages: calling Fs_IOControl.\n");
  1739. #endif                /* DEBUG */
  1740.     if (MigSetAlarm() < 0) {
  1741.         fprintf(stderr,
  1742.             "Error setting alarm for contact with global migd.\n");
  1743.         return(FALSE);
  1744.     }
  1745.     status = Fs_IOControl(migdGlobalDesc, IOC_MIG_GET_UPDATE,
  1746.                   0, (char *) NULL, sizeof(int),
  1747.                   (char *) &msgHostID);
  1748.     if (MigClearAlarm() < 0) {
  1749.         fprintf(stderr,
  1750.             "Error clearing alarm for contact with migd.\n");
  1751.     }
  1752.         
  1753.         
  1754. #ifdef DEBUG
  1755.     fprintf(stderr, "CheckMessages: Fs_IOControl returned %x.\n",
  1756.         status);
  1757. #endif                /* DEBUG */
  1758.     if (status != SUCCESS) {
  1759.         fprintf(stderr,
  1760.             "CheckMessages: error during ioctl to global master: %s\n",
  1761.             Stat_GetMsg(status));
  1762.         return(-1);
  1763.     }
  1764.     return(msgHostID);
  1765.     }
  1766. }
  1767.